package com.stanwind.dbselector.aop;

import com.stanwind.dbselector.util.ExecutorPluginUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Properties;

import static com.stanwind.dbselector.util.ExecutorPluginUtils.getSqlByInvocation;

/**
 * NodeInterceptor 拦截mybatis 更改sql
 *
 * @author : stan
 * @version : 1.0
 * @date :  2022/8/19 5:26 PM
 **/
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})

})
@Component
public class NodeInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String hint = NodeContext.getContext().getHint();
        if (StringUtils.isEmpty(hint)) {
            return invocation.proceed();
        }

        String processSql = getSqlByInvocation(invocation);
        log.debug("schema替换前：{}", processSql);
        // 执行自定义修改sql操作
        String sql2Reset = hint + processSql;
        log.debug("schema替换后：{}", sql2Reset);
        // 替换sql
        ExecutorPluginUtils.resetSql2Invocation(invocation, sql2Reset);

        return invocation.proceed();
    }

    @Override
    public void setProperties(Properties properties) {

    }

    @Override
    public Object plugin(Object target) {
        // 只对要拦截制定类型的对象生成代理
        if (target instanceof Executor) {
            // 调用插件
            return Plugin.wrap(target, this);
        }

        return target;
    }
}
